package edu.napier.soc.xfdm.uci;

import com.csvreader.CsvWriter;
import edu.napier.soc.xfdm.model.Database;
import edu.napier.soc.xfdm.parser.*;
import org.syntax.jedit.JEditTextArea;
import org.syntax.jedit.TextAreaDefaults;

import javax.swing.*;
import javax.swing.JToolBar.Separator;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author Thomas
 */
public class MainGUI extends javax.swing.JFrame {

	private Database d = new Database();
	private final XFDMParser xfdm = new XFDMParser(d);
	private ImageIcon icon;
	private List<JTable> tables = new ArrayList<JTable>();
	private List<JPanel> panels = new ArrayList<JPanel>();

	/**
	 * Creates new form MainGUI
	 */
	public MainGUI() {
		icon = new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/" +
				"resources/icon.png"));
		initComponents();
		setLocationRelativeTo(null);
		editTextArea.requestFocusInWindow();
		this.setTransferHandler(handler);

	}

	@SuppressWarnings("unchecked")
	// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
	private void initComponents() {
		GridBagConstraints gridBagConstraints;

		mainPanel = new JPanel();
		splitPane = new JSplitPane();
		statementsPanel = new JPanel();
		consolePanel = new JPanel();
		consoleScrollPane = new JScrollPane();
		consoleTextArea = new JTextArea();
		TextAreaDefaults tad = TextAreaDefaults.getDefaults();
		tad.lineHighlight = false;
		editTextArea = new JEditTextArea();
		tabbedPane = new JTabbedPane();
		tempPanel = new JPanel();
		progressPanel = new JPanel();
		progressBar = new JProgressBar();
		stopButton = new JButton();
		bottomPanel = new JPanel();
		csvButton = new JButton();
		expandButton = new JButton();
		toolBar = new JToolBar();
		executeButton = new JButton();
		clearButton = new JButton();
		loadStatementsButton = new JButton();
		separator = new Separator();
		dumpButton = new JButton();
		loadButton = new JButton();
		deleteButton = new JButton();
		seperator4 = new Separator();
		statementModeButton = new JToggleButton();
		seperator5 = new Separator();
		visualButton = new JButton();
		menuBar = new JMenuBar();
		fileMenu = new JMenu();
		openMenuItem = new JMenuItem();
		saveMenuItem = new JMenuItem();
		deleteMenuItem = new JMenuItem();
		seperator2 = new JSeparator();
		loadStatementsMenuItem = new JMenuItem();
		seperator3 = new JSeparator();
		jMenuItem2 = new JMenuItem();
		runMenu = new JMenu();
		executeMenuItem = new JMenuItem();
		clearMenuItem = new JMenuItem();
		toolsMenu = new JMenu();
		visualMenuItem = new JMenuItem();
		helpMenu = new JMenu();
		aboutMenuItem = new JMenuItem();

		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setTitle("eXtended Functional Data Model");
		setIconImage(icon.getImage());
		setName("mainFrame"); // NOI18N

		mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		mainPanel.setLayout(new GridBagLayout());

		splitPane.setBorder(null);
		splitPane.setDividerLocation(-5);
		splitPane.setDividerSize(15);
		splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
		splitPane.setResizeWeight(0.6);
		splitPane.setOneTouchExpandable(true);

		statementsPanel.setPreferredSize(new Dimension(800, 600));
		statementsPanel.setLayout(new GridBagLayout());

		consolePanel
				.setBorder(BorderFactory.createTitledBorder("XFDM Console:"));
		consolePanel.setLayout(new BorderLayout());

		consoleTextArea.setEditable(false);
		consoleScrollPane.setViewportView(consoleTextArea);

		consolePanel.add(consoleScrollPane, BorderLayout.CENTER);

		gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.gridx = 0;
		gridBagConstraints.gridy = 1;
		gridBagConstraints.fill = GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		gridBagConstraints.weighty = 0.5;
		statementsPanel.add(consolePanel, gridBagConstraints);

		editTextArea.setBorder(BorderFactory.createEtchedBorder());
		editTextArea.setDoubleBuffered(true);
		editTextArea.setMaximumSize(new Dimension(600, 32767));
		editTextArea.setMinimumSize(new Dimension(4, 22));
		editTextArea.setPreferredSize(new Dimension(4, 22));
		editTextArea.setTokenMarker(new XFDMTokenMarker());
		editTextArea.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent evt) {
				editTextAreaKeyReleased(evt);
			}
		});
		gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.gridx = 0;
		gridBagConstraints.gridy = 0;
		gridBagConstraints.fill = GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		gridBagConstraints.weighty = 1.0;
		statementsPanel.add(editTextArea, gridBagConstraints);

		splitPane.setLeftComponent(statementsPanel);

		tabbedPane.setTabPlacement(JTabbedPane.LEFT);
		tabbedPane.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent evt) {
				tabbedPaneStateChanged(evt);
			}
		});

		tempPanel.setMaximumSize(new Dimension(0, 0));
		tempPanel.setMinimumSize(new Dimension(0, 0));
		tempPanel.setPreferredSize(new Dimension(0, 0));
		tabbedPane.addTab("blank", tempPanel);

		splitPane.setRightComponent(tabbedPane);

		gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.gridx = 0;
		gridBagConstraints.gridy = 0;
		gridBagConstraints.fill = GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		gridBagConstraints.weighty = 1.0;
		mainPanel.add(splitPane, gridBagConstraints);

		progressBar.setMinimumSize(progressBar.getPreferredSize());
		progressBar.setStringPainted(true);
		progressPanel.add(progressBar);

		stopButton.setText("stop");
		stopButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				stopButtonActionPerformed(evt);
			}
		});
		progressPanel.add(stopButton);

		progressPanel.setVisible(false);

		gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.gridx = 0;
		gridBagConstraints.gridy = 2;
		gridBagConstraints.anchor = GridBagConstraints.LAST_LINE_END;
		mainPanel.add(progressPanel, gridBagConstraints);

		bottomPanel.setVisible(false);
		bottomPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

		csvButton.setText("output to csv");
		csvButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				csvButtonActionPerformed(evt);
			}
		});
		bottomPanel.add(csvButton);

		expandButton.setText("expand");
		expandButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				expandButtonActionPerformed(evt);
			}
		});
		bottomPanel.add(expandButton);

		gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.gridx = 0;
		gridBagConstraints.gridy = 1;
		gridBagConstraints.fill = GridBagConstraints.BOTH;
		mainPanel.add(bottomPanel, gridBagConstraints);

		getContentPane().add(mainPanel, BorderLayout.CENTER);

		toolBar.setRollover(true);

		executeButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/run.png"))); // NOI18N
		executeButton.setToolTipText("Execute Statements (F5)");
		executeButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				executeButtonActionPerformed(evt);
			}
		});
		toolBar.add(executeButton);

		clearButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/clear.png"))); // NOI18N
		clearButton.setToolTipText("Clear Statements");
		clearButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				clearButtonActionPerformed(evt);
			}
		});
		toolBar.add(clearButton);

		loadStatementsButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/text.png"))); // NOI18N
		loadStatementsButton.setToolTipText("Open Statements...");
		loadStatementsButton.setFocusable(false);
		loadStatementsButton.setHorizontalTextPosition(SwingConstants.CENTER);
		loadStatementsButton.setVerticalTextPosition(SwingConstants.BOTTOM);
		loadStatementsButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				loadStatementsButtonActionPerformed(evt);
			}
		});
		toolBar.add(loadStatementsButton);
		toolBar.add(separator);

		dumpButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/save.png"))); // NOI18N
		dumpButton.setToolTipText("Save Database... (CTRL + S)");
		dumpButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				dumpButtonActionPerformed(evt);
			}
		});
		toolBar.add(dumpButton);

		loadButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/open.png"))); // NOI18N
		loadButton.setToolTipText("Open Database... (CTRL + O)");
		loadButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				loadButtonActionPerformed(evt);
			}
		});
		toolBar.add(loadButton);

		deleteButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/delete.png"))); // NOI18N
		deleteButton.setToolTipText("Clear Database...");
		deleteButton.setFocusable(false);
		deleteButton.setHorizontalTextPosition(SwingConstants.CENTER);
		deleteButton.setVerticalTextPosition(SwingConstants.BOTTOM);
		deleteButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				deleteButtonActionPerformed(evt);
			}
		});
		toolBar.add(deleteButton);
		toolBar.add(seperator4);

		statementModeButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/multi.png"))); // NOI18N
		statementModeButton.setToolTipText("Statement Mode (Single or Multi)");
		statementModeButton.setFocusable(false);
		statementModeButton.setHorizontalTextPosition(SwingConstants.CENTER);
		statementModeButton.setSelectedIcon(new ImageIcon(
				getClass().getResource(
						"/edu/napier/soc/xfdm/resources/single.png"))); // NOI18N
		statementModeButton.setVerticalTextPosition(SwingConstants.BOTTOM);
		toolBar.add(statementModeButton);
		toolBar.add(seperator5);

		visualButton.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/rels.png"))); // NOI18N
		visualButton.setToolTipText("Visual Viewer");
		visualButton.setFocusable(false);
		visualButton.setHorizontalTextPosition(SwingConstants.CENTER);
		visualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
		visualButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				visualButtonActionPerformed(evt);
			}
		});
		toolBar.add(visualButton);

		getContentPane().add(toolBar, BorderLayout.PAGE_START);

		fileMenu.setText("File");
		fileMenu.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				fileMenuActionPerformed(evt);
			}
		});

		openMenuItem.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
		openMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/openS.png"))); // NOI18N
		openMenuItem.setText("Open Database...");
		openMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				openMenuItemActionPerformed(evt);
			}
		});
		fileMenu.add(openMenuItem);

		saveMenuItem.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
		saveMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/saveS.png"))); // NOI18N
		saveMenuItem.setText("Save As...");
		saveMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				saveMenuItemActionPerformed(evt);
			}
		});
		fileMenu.add(saveMenuItem);

		deleteMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/deleteS.png"))); // NOI18N
		deleteMenuItem.setText("Clear Database...");
		deleteMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				deleteMenuItemActionPerformed(evt);
			}
		});
		fileMenu.add(deleteMenuItem);
		fileMenu.add(seperator2);

		loadStatementsMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/textS.png"))); // NOI18N
		loadStatementsMenuItem.setText("Open Statements...");
		loadStatementsMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				loadStatementsMenuItemActionPerformed(evt);
			}
		});
		fileMenu.add(loadStatementsMenuItem);
		fileMenu.add(seperator3);

		jMenuItem2.setAccelerator(
				KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK));
		jMenuItem2.setText("Exit");
		jMenuItem2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				jMenuItem2ActionPerformed(evt);
			}
		});
		fileMenu.add(jMenuItem2);

		menuBar.add(fileMenu);

		runMenu.setText("Run");

		executeMenuItem
				.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
		executeMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/playS.png"))); // NOI18N
		executeMenuItem.setText("Execute Statements");
		executeMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				executeMenuItemActionPerformed(evt);
			}
		});
		runMenu.add(executeMenuItem);

		clearMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/clearS.png"))); // NOI18N
		clearMenuItem.setText("Clear Statements");
		clearMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				clearMenuItemActionPerformed(evt);
			}
		});
		runMenu.add(clearMenuItem);

		menuBar.add(runMenu);

		toolsMenu.setText("Tools");

		visualMenuItem.setIcon(new ImageIcon(getClass().getResource(
				"/edu/napier/soc/xfdm/resources/visualS.png"))); // NOI18N
		visualMenuItem.setText("Visual Viewer");
		visualMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				visualMenuItemActionPerformed(evt);
			}
		});
		toolsMenu.add(visualMenuItem);

		menuBar.add(toolsMenu);

		helpMenu.setText("Help");

		aboutMenuItem.setText("About");
		aboutMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				aboutMenuItemActionPerformed(evt);
			}
		});
		helpMenu.add(aboutMenuItem);

		menuBar.add(helpMenu);

		setJMenuBar(menuBar);

		pack();
	}// </editor-fold>//GEN-END:initComponents

	private void clearButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
		editTextArea.setText("");
		editTextArea.requestFocusInWindow();
	}//GEN-LAST:event_clearButtonActionPerformed

	private void executeButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_executeButtonActionPerformed
		progressPanel.setVisible(true);
		new SwingThread(editTextArea.getText(), this).execute();
	}//GEN-LAST:event_executeButtonActionPerformed

	private void dumpButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_dumpButtonActionPerformed
		File f = saveFile(this,
				new FileNameExtensionFilter("XFDM Database (*.xfm)", "xfm"));

		if (f == null) {
			return;
		}
		if (!f.getName().toLowerCase().endsWith(".xfm")) {
			f = new File(f.getAbsolutePath() + ".xfm");
		}
		ObjectOutputStream out = null;

		try {
			try {
				out = new ObjectOutputStream(new FileOutputStream(f));
				out.writeObject(d);
			} finally {
				if (out != null) {
					out.close();
				}
			}
			consoleTextArea.setText("Database saved to disk.");
		} catch (IOException ex) {
			consoleTextArea.setText("Problem saving the database: "
					+ ex.getMessage());
		}
	}//GEN-LAST:event_dumpButtonActionPerformed

	public static File saveFile(Component parent, FileFilter... ff) {
		JFileChooser jfc = new JFileChooser(".");

		for (FileFilter f : ff) {
			jfc.addChoosableFileFilter(f);
		}
		jfc.setAcceptAllFileFilterUsed(false);
		int opt = jfc.showSaveDialog(parent);

		if (opt == JFileChooser.CANCEL_OPTION) {
			return null;
		}

		if (opt == JFileChooser.APPROVE_OPTION) {
			return jfc.getSelectedFile();
		}

		return null;
	}

	public static File openFile(Component parent, boolean acceptAll,
	                            FileFilter... ff) {
		JFileChooser jfc = new JFileChooser(".");

		jfc.setAcceptAllFileFilterUsed(acceptAll);
		for (FileFilter f : ff) {
			jfc.addChoosableFileFilter(f);
			jfc.setFileFilter(f);
		}
		int opt = jfc.showOpenDialog(parent);

		if (opt == JFileChooser.CANCEL_OPTION) {
			return null;
		}

		if (opt == JFileChooser.APPROVE_OPTION) {
			return jfc.getSelectedFile();
		}

		return null;
	}

	private void loadButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_loadButtonActionPerformed
		File f = openFile(this, false,
				new FileNameExtensionFilter("XFDM Database (*.xfm)", "xfm"));

		if (f == null || !f.exists()) {
			return;
		}

		loadDatabase(f);
	}//GEN-LAST:event_loadButtonActionPerformed

	private SwingWorker<Void, Void> loadDB;

	public void loadDatabase(final File f) {
		if (loadDB == null) {
			loadDB = new SwingWorker<Void, Void>() {
				private volatile String message;

				@Override
				protected Void doInBackground() throws Exception {
					ObjectInputStream in = null;

					try {
						try {
							in = new ObjectInputStream(new FileInputStream(f));
							d = (Database) in.readObject();
							xfdm.setDatabase(d);
						} finally {
							if (in != null) {
								in.close();
							}
						}
						message = "XFDM database loaded into memory.";
					} catch (ClassNotFoundException ex) {
						message = "Loading encountered an error: "
								+ ex.getMessage();
					} catch (IOException ex) {
						message = "Loading encountered an error: "
								+ ex.getMessage();
					} catch (Exception ex) {
						ex.printStackTrace();
					}
					return null;
				}

				@Override
				protected void done() {
					progressPanel.setVisible(false);
					progressBar.setIndeterminate(false);
					progressBar.setStringPainted(true);
					stopButton.setVisible(true);
					consoleTextArea.setText(message);
				}
			};
		}

		switch (loadDB.getState()) {
			case STARTED:
				return;
			case DONE:
			case PENDING:
				progressPanel.setVisible(true);
				progressBar.setIndeterminate(true);
				progressBar.setStringPainted(false);
				stopButton.setVisible(false);
				loadDB.execute();
		}
	}

	private void stopButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
		xfdm.stopRequest();
	}//GEN-LAST:event_stopButtonActionPerformed

	private void tabbedPaneStateChanged(
			ChangeEvent evt) {//GEN-FIRST:event_tabbedPaneStateChanged
		bottomPanel.setVisible(tabbedPane.getTabCount() > 0);
	}//GEN-LAST:event_tabbedPaneStateChanged

	private void expandButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_expandButtonActionPerformed
		final int index = panels.indexOf(tabbedPane.getSelectedComponent());
		final JFrame jf =
				new JFrame("Query " + (index + 1));
		jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		jf.setIconImage(icon.getImage());
		jf.add(tabbedPane.getSelectedComponent());
		jf.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				if (tabbedPane.getTabCount() >= index) {
					tabbedPane.insertTab("Query " + (index + 1), null,
							panels.get(index), null, index);
				} else {
					tabbedPane
							.addTab("Query " + (index + 1), panels.get(index));
				}
			}
		});
		jf.pack();
		jf.setVisible(true);
	}//GEN-LAST:event_expandButtonActionPerformed

	private void csvButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_csvButtonActionPerformed
		JTable jt = tables.get(tabbedPane.getSelectedIndex());
		File f = saveFile(this,
				new FileNameExtensionFilter("CSV File (*.csv)", "csv"));

		if (f == null) {
			return;
		}

		if (!f.getName().toLowerCase().endsWith(".csv")) {
			f = new File(f.getAbsolutePath() + ".csv");
		}
		CsvWriter csv = new CsvWriter(f.getAbsolutePath());
		TableModel dtm = jt.getModel();

		try {
			String[] cols = new String[dtm.getColumnCount()];
			for (int i = 0; i < dtm.getColumnCount(); i++) {
				cols[i] = dtm.getColumnName(i);
			}
			csv.writeRecord(cols);
			for (int i = 0; i < dtm.getRowCount(); i++) {
				String[] record = new String[dtm.getColumnCount()];
				for (int j = 0; j < dtm.getColumnCount(); j++) {
					Object value = dtm.getValueAt(i, j);

					if (value != null) {
						record[j] = value.toString();
					}
				}
				csv.writeRecord(record);
				csv.flush();
				consoleTextArea.setText("Query output to CSV.");
			}
		} catch (IOException ex) {
			consoleTextArea.setText("Problem writing to CSV: "
					+ ex.getMessage());
		} finally {
			csv.close();
		}
	}//GEN-LAST:event_csvButtonActionPerformed

	private void editTextAreaKeyReleased(
			KeyEvent evt) {//GEN-FIRST:event_editTextAreaKeyReleased
		if (!statementModeButton.isSelected()) {
			return;
		}
		if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
			String statements = editTextArea.getText().trim();
			if (statements.endsWith(";") &&
					editTextArea.getCaretPosition() >= statements.length()) {
				executeButton.doClick();
			}
		}
	}//GEN-LAST:event_editTextAreaKeyReleased

	private void aboutMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_aboutMenuItemActionPerformed
		new AboutDialog(this, true).setVisible(true);
	}//GEN-LAST:event_aboutMenuItemActionPerformed

	private void deleteButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
		String message =
				"Are you sure you want to clear the database of all\n" +
						"entity types, entities, functions and constraints?";
		String title = "Database Delete Confirmation";
		int opt = JOptionPane.showConfirmDialog(this, message, title,
				JOptionPane.YES_NO_CANCEL_OPTION);

		switch (opt) {
			case JOptionPane.YES_OPTION:
				d.clear();
				consoleTextArea.setText("Database deleted.");
				break;
			default:
				consoleTextArea.setText("Database not deleted.");
				break;
		}
	}//GEN-LAST:event_deleteButtonActionPerformed

	private void jMenuItem2ActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed
		System.exit(0);
	}//GEN-LAST:event_jMenuItem2ActionPerformed

	private void visualMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_visualMenuItemActionPerformed
		new DatabaseView(d.getEntityTypes(), this).setVisible(true);
	}//GEN-LAST:event_visualMenuItemActionPerformed

	private void loadStatementsButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_loadStatementsButtonActionPerformed
		File f = openFile(this, true,
				new FileNameExtensionFilter("Text Documents (*.txt)", "txt"));

		if (f == null) {
			return;
		}

		loadStatements(f);
	}//GEN-LAST:event_loadStatementsButtonActionPerformed

	public void loadStatements(File f) {
		try {
			String text = getFileText(f);
			editTextArea.setText(text);
			editTextArea.setCaretPosition(0);
		} catch (IOException e) {
			consoleTextArea.setText("Error loading file: " + e.getMessage());
		}
	}

	private void openMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_openMenuItemActionPerformed
		loadButtonActionPerformed(null);
	}//GEN-LAST:event_openMenuItemActionPerformed

	private void saveMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_saveMenuItemActionPerformed
		dumpButtonActionPerformed(null);
	}//GEN-LAST:event_saveMenuItemActionPerformed

	private void fileMenuActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_fileMenuActionPerformed
		deleteButtonActionPerformed(null);
	}//GEN-LAST:event_fileMenuActionPerformed

	private void loadStatementsMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_loadStatementsMenuItemActionPerformed
		loadStatementsButtonActionPerformed(null);
	}//GEN-LAST:event_loadStatementsMenuItemActionPerformed

	private void executeMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_executeMenuItemActionPerformed
		executeButtonActionPerformed(null);
	}//GEN-LAST:event_executeMenuItemActionPerformed

	private void clearMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_clearMenuItemActionPerformed
		clearButtonActionPerformed(null);
	}//GEN-LAST:event_clearMenuItemActionPerformed

	private void deleteMenuItemActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_deleteMenuItemActionPerformed
		deleteButtonActionPerformed(null);
	}//GEN-LAST:event_deleteMenuItemActionPerformed

	public void showConsoleCommands(String text) {
		JTextArea jt = new JTextArea(text);
		jt.setFont(new Font("monospaced", Font.PLAIN, 12));
		jt.setEditable(false);
		jt.setOpaque(false);
		JOptionPane.showMessageDialog(this, jt, "Console Options",
				JOptionPane.INFORMATION_MESSAGE);
	}

	private void visualButtonActionPerformed(
			ActionEvent evt) {//GEN-FIRST:event_visualButtonActionPerformed
		visualMenuItemActionPerformed(null);
	}//GEN-LAST:event_visualButtonActionPerformed

	private String showError(String command, InterpreterException ex) {
		StringBuilder sb = new StringBuilder();
		sb.append(ex.getCommand() + '\n');

		for (int i = 0; i < ex.getColumn() - 1; i++) {
			sb.append(' ');
		}
		int pos = command.indexOf(ex.getCommand());

		if (pos != -1) {
			editTextArea.select(0, pos);
		}
		sb.append('^');
		sb.append("\n\n" + ex.getMessage() + "\n\n");
		sb.append(ex.getExecuted() + " statements were executed.");

		return sb.toString();
	}

	private String showError(String command, ExecuteException ex) {
		String[] commands = command.split("\n");
		StringBuilder sb = new StringBuilder(commands[ex.getLine() - 1] + "\n");

		for (int i = 0; i < ex.getColumn() - 1; i++) {
			sb.append(' ');
		}
		sb.append('^');
		sb.append("\n\n" + ex.getMessage() + "\n\n");
		sb.append("0 statements were executed.");

		return sb.toString();
	}

	// Variables declaration - do not modify//GEN-BEGIN:variables
	private JMenuItem aboutMenuItem;
	private JPanel bottomPanel;
	private JButton clearButton;
	private JMenuItem clearMenuItem;
	private JPanel consolePanel;
	private JScrollPane consoleScrollPane;
	private JTextArea consoleTextArea;
	private JButton csvButton;
	private JButton deleteButton;
	private JMenuItem deleteMenuItem;
	private JButton dumpButton;
	private JEditTextArea editTextArea;
	private JButton executeButton;
	private JMenuItem executeMenuItem;
	private JButton expandButton;
	private JMenu fileMenu;
	private JMenu helpMenu;
	private JMenuItem jMenuItem2;
	private JButton loadButton;
	private JButton loadStatementsButton;
	private JMenuItem loadStatementsMenuItem;
	private JPanel mainPanel;
	private JMenuBar menuBar;
	private JMenuItem openMenuItem;
	private JProgressBar progressBar;
	private JPanel progressPanel;
	private JMenu runMenu;
	private JMenuItem saveMenuItem;
	private Separator separator;
	private JSeparator seperator2;
	private JSeparator seperator3;
	private Separator seperator4;
	private Separator seperator5;
	private JSplitPane splitPane;
	private JToggleButton statementModeButton;
	private JPanel statementsPanel;
	private JButton stopButton;
	private JTabbedPane tabbedPane;
	private JPanel tempPanel;
	private JToolBar toolBar;
	private JMenu toolsMenu;
	private JButton visualButton;
	private JMenuItem visualMenuItem;
	// End of variables declaration//GEN-END:variables

	private class SwingThread extends SwingWorker<Void, Integer> {
		private final QueryResultSet q = new QueryResultSet();
		private final String commands;
		private volatile String message = "";
		private volatile boolean allExecuted = false;
		private volatile boolean allDone = false;
		private final Frame owner;
		private volatile RuntimeException t;
		private final Executor executor = Executors.newSingleThreadExecutor();

		public SwingThread(String commands, Frame owner) {
			this.commands = commands;
			this.owner = owner;
		}

		@Override
		protected Void doInBackground() {
			try {
				monitorProgress();
				xfdm.parseStatements(commands, q);
				message += "All statements executed successfully.";
				allExecuted = true;
			} catch (final InterpreterException ex) {
				message += showError(commands, ex);
			} catch (final ExecuteException ex) {
				message += showError(commands, ex);
			} catch (RuntimeException ex) {
				t = ex;
			} catch (Exception ex) {
				message += ex.getMessage();
			}
			return null;
		}

		private void monitorProgress() {
			executor.execute(new Runnable() {
				public void run() {
					try {
						while (!allDone) {
							Thread.sleep(5);
							publish(xfdm.getExecuted(),
									xfdm.getNumOfStatements());
						}
					} catch (InterruptedException ex) {
						System.err.println("Fucked");
					}
				}
			});
		}

		protected void publish(int progress, int total) {
			progressBar.setString(progress + " statements executed");
			progressBar.setMaximum(total);
			progressBar.setValue(progress);
		}

		@Override
		protected void done() {
			allDone = true;
			if (t != null) {
				throw t;
			}
			progressBar.setIndeterminate(false);
			progressPanel.setVisible(false);
			consoleTextArea.setText(message);
			consoleTextArea.append("\n----------\n");

			String event;
			while ((event = q.nextEvent()) != null) {
				consoleTextArea.append(event);
				consoleTextArea.append("\n----------\n");
			}

			tabbedPane.removeAll();
			tables.clear();
			panels.clear();

			if (allExecuted) {
				editTextArea.selectAll();
			}

			if (q.size() == 0) {
				tabbedPane.add("Blank", tempPanel);
				bottomPanel.setVisible(false);
				q.clear();
				splitPane.resetToPreferredSizes();
				return;
			}

			bottomPanel.setVisible(true);
			QueryResult r;

			while ((r = q.nextQueryResult()) != null) {
				JPanel p = new JPanel(new BorderLayout(5, 5));
				JLabel l = new JLabel(r.getQuery(), 0);
				JTable tb = new NonEditableTable(
						r.getRows(), r.getColumnNames().toArray());
				tb.setRowSorter(new TableRowSorter<TableModel>(tb.getModel()));
				JScrollPane s = new JScrollPane(tb);
				p.add(l, BorderLayout.NORTH);
				p.add(s, BorderLayout.CENTER);
				tables.add(tb);
				panels.add(p);
				tabbedPane.add("Query " + (tabbedPane.getTabCount() + 1), p);
			}
			q.clear();
			splitPane.resetToPreferredSizes();
			editTextArea.requestFocusInWindow();
		}

	}

	private static class NonEditableTable extends JTable {

		public NonEditableTable(Object[][] rowData, Object[] columnNames) {
			super(rowData, columnNames);
		}

		@Override
		public boolean isCellEditable(int row, int col) {
			return false;
		}
	}

	private TransferHandler handler = new TransferHandler() {

		@Override
		public boolean canImport(TransferHandler.TransferSupport support) {
			return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
		}

		@Override
		public boolean importData(TransferHandler.TransferSupport support) {
			if (!canImport(support)) {
				return false;
			}

			Transferable t = support.getTransferable();

			try {
				List<File> l =
						(List) t.getTransferData(DataFlavor.javaFileListFlavor);

				if (l.get(0).getName().toLowerCase().endsWith(".xfm")) {
					loadDatabase(l.get(0));
					return true;
				}
				editTextArea.setText(getFileText(l.get(0)));
			} catch (UnsupportedFlavorException e) {
				return false;
			} catch (IOException e) {
				return false;
			}
			return true;
		}
	};

	public String getFileText(File f) throws IOException {
		BufferedReader in = null;
		StringBuilder sb = new StringBuilder();

		try {
			in = new BufferedReader(new FileReader(f));
			String line;
			while ((line = in.readLine()) != null) {
				sb.append(line + '\n');
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}

		return sb.toString();
	}
}
